package mqtt

import (
	mqtts "github.com/eclipse/paho.mqtt.golang"
	"github.com/satori/go.uuid"
	"github.com/sirupsen/logrus"
	"time"
)

type MqttTopicStore struct {
	qos byte
	fun func(topic string, payload []byte)
}
type MqttClient struct {
	Id       string
	Url      string                    //mqtt服务器地址  例如 tcp://broken-cn.emqx.com
	Username string                    //mqtt 登录用户名
	Password string                    //mqtt 登录密码
	Timeout  int                       //超时时间
	Retry    int                       //重试次数
	topics   map[string]MqttTopicStore //订阅的主题
	client   mqtts.Client              //客户端
}

func NewMqttClient(url string, id, username, password string) *MqttClient {
	return &MqttClient{
		Id:       id,
		Url:      url,
		Username: username,
		Password: password,
		Timeout:  30,
		topics:   make(map[string]MqttTopicStore),
		Retry:    3,
	}
}

// 创建全局mqtt publish消息处理 handler
var messagePubHandler mqtts.MessageHandler = func(client mqtts.Client, msg mqtts.Message) {
	logrus.Info("Push Message:")
	logrus.Info("TOPIC: %s\n", msg.Topic())
	logrus.Info("MSG: %s\n", msg.Payload())
}

// 创建全局mqtt sub消息处理 handler
var messageSubHandler mqtts.MessageHandler = func(client mqtts.Client, msg mqtts.Message) {
	logrus.Debug("收到订阅消息:")
	logrus.Debug("Sub Client Topic : %s \n", msg.Topic())
	logrus.Debug("Sub Client msg : %s \n", msg.Payload())
}

// 连接的回掉函数
func (mqtt *MqttClient) connectHandler(client mqtts.Client) {
	logrus.Debug("新的连接!" + " Connected")
	for topic, st := range mqtt.topics {
		mqtt.Subscripte(topic, st.qos, st.fun)
	}
}

// 丢失连接的回掉函数
var connectLostHandler mqtts.ConnectionLostHandler = func(client mqtts.Client, err error) {
	logrus.Debug("Connect loss: %v\n", err)
}

func (mqtt *MqttClient) Connect() *MqttClient {
	opts := mqtts.NewClientOptions().AddBroker(mqtt.Url)
	opts.SetKeepAlive(60 * time.Second)

	id := uuid.NewV4()
	// Message callback handler，在没有任何订阅时，发布端调用此函数
	opts.SetDefaultPublishHandler(messagePubHandler)
	opts.Username = mqtt.Username
	opts.Password = mqtt.Password
	opts.SetPingTimeout(60 * time.Second)
	opts.OnConnect = mqtt.connectHandler
	opts.OnConnectionLost = connectLostHandler
	if len(mqtt.Id) != 0 {
		opts.ClientID = mqtt.Id
	} else {
		opts.ClientID = id.String()
	}
	mqtt.client = mqtts.NewClient(opts)
	if token := mqtt.client.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}

	return mqtt
}

func (mqtt *MqttClient) Disconnect() {
	mqtt.client.Disconnect(250)
}

func (mqtt *MqttClient) Publish(topic string, qos byte, payload interface{}) *MqttClient {
	token := mqtt.client.Publish(topic, qos, false, payload)
	token.Wait()
	logrus.Debug("Push Data : "+topic, "Data Size is ", payload)

	return mqtt
}

func (mqtt *MqttClient) Subscripte(topic string, qos byte, handleFun func(topic string, payload []byte)) bool {
	// 订阅消息
	if token := mqtt.client.Subscribe(topic, qos, func(client mqtts.Client, msg mqtts.Message) {
		logrus.Debug("Receive Subscribe Message :")
		logrus.Debug("Sub Client Topic : %s, Data size is  %d \n", msg.Topic(), len(msg.Payload()))

		if len(msg.Payload()) > 0 {
			handleFun(msg.Topic(), msg.Payload())
		}
	}); token.Wait() && token.Error() != nil {
		logrus.Debug(token.Error())
		return false
	}

	mqtt.topics[topic] = MqttTopicStore{qos, handleFun}
	return true
}

func (mqtt *MqttClient) UnSubscripte(topic string) bool {
	if token := mqtt.client.Unsubscribe(topic); token.Wait() && token.Error() != nil {
		logrus.Debug(token.Error())
		return false
	}
	return true
}
